技巧106 限制容器的内存使用

运行容器的时候,Docker 会允许它从宿主机分配尽可能多的内存。通常这是我们想要的效果(也是与虚拟机相比的巨大优势,虚拟机分配内存的方式并不灵活)。但是有时候应用程序可能会脱离控制,分配了太多内存,当机器开始交换内存的时候就会死机。这很烦人,我们以前也发生了很多次。我们想要一种能限制容器内存消耗的方式来防止这件事。

问题

想要限制容器的内存消耗。

解决方案

docker run 使用 -m/--memory 参数。

如果正在运行Ubuntu,很可能内存限制能力默认并没有启用。执行 docker info 来检查。如果输出中有一行在警告 No swap limit support ,就要先做些准备工作。注意,这些改变可能对机器上所有的应用程序都有性能影响,参见Ubuntu安装文档,获取更多信息。

简单来说,需要在启动时就告诉内核,希望这些限制可用。为了达到这个目的,需要如下修改/etc/default/grub。如果 GRUB_CMDLINE_LINUX 已经有值了,在末尾加上新的:

-GRUB_CMDLINE_LINUX=""
+GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"

现在需要执行 sudo update-grub 并重启计算机。执行 docker info 应该不会再得到警告了,现在可以继续正题了。

首先,简单粗暴地展示一下使用4 MB的最低可能内存限制确实有用,如代码清单15-5所示。

代码清单15-5 对容器设置尽可能小的内存限额

$ docker run -it -m 4m ubuntu:14.04 bash  ⇽--- 以4 MB内存限制运行容器
 root@cffc126297e2:/# \
python3 -c 'open("/dev/zero").read(10*1024*1024)'  ⇽--- 试着把10 MB加载到内存
 Killed  ⇽--- 进程消耗了太多内存,因此被杀死了
 root@e9f13cacd42f:/# \
A=$(dd if=/dev/zero bs=1M count=10 | base64)   ⇽--- 试着把10 MB的内存直接加载到bash中
 $  ⇽---  bash也被杀死了,容器因而退出
 $ echo $?  ⇽--- 检查退出码
 137  ⇽--- 退出码非零,表明容器因错退出

这种限制有一个缺陷。为了展示这一点,我们要使用jess/stress镜像,这一镜像里面包含有stress,一个用来测试系统限制的工具。

提示

jess/stress是一个用来测试在容器上施加的资源限制的有用镜像。想要实验更多的话,用这个镜像试一下之前的技巧吧。

如果执行下面的命令,你会惊讶地发现它立刻就不存在了:

docker run -m 100m jess/stress --vm 1 --vm-bytes 150M --vm-hang 0

你已经让Docker限制容器到100 MB了,已经让 stress 占用150 MB了。可以通过执行下面的命令验证 stress 正在如期运行:

docker top <container_id> -eo pid,size,args

大小(size)那一栏是以KB为单位显示的,展示出容器确实占用了150 MB内存,问题来了,为什么它还没有被杀死呢。原来是Docker双重保留了内存,一半给物理内存,一半用于内存交换。如果使用下面的命令,容器会立刻终止:

docker run -m 100m jess/stress --vm 1 --vm-bytes 250M --vm-hang 0

这种双重保留机制是默认设置,可以通过 --memory-swap 参数来控制,该参数指定了总体虚拟内存的大小(内存+交换)。例如,想要完全消除交换内存的使用,应当把 --memory--memory-swap 设定为同一大小。在Docker官方文档的“run”参考中可以找到更多的例子。

讨论

内存限制对任何运维(或DevOps)团队来说都是最热门的话题。错误配置或随意配置的容器经常会耗尽分配或预留的内存(我在看着你呢,Java开发者!), 需要写好FAQ和运行指南,已备直接用户号啕大哭的时候查看。

对这种情况有意识,对于支持公共平台很有好处,也可以让用户了解到底在发生什么。

results matching ""

    No results matching ""